package org.apache.flink.runtime.resourceconsumer;

import static org.apache.flink.util.Preconditions.checkArgument;
import static org.apache.flink.util.Preconditions.checkNotNull;

import java.util.Objects;

import org.apache.flink.runtime.clusterframework.types.ConsumeItemID;
import org.apache.flink.shaded.netty4.io.netty.buffer.ByteBuf;

/**
 * Unique identifier for the attempt to consume a resource. Multiple attempts happen in cases of
 * failures and recovery.
 */
public class ConsumeAttemptID implements java.io.Serializable {

    private static final long serialVersionUID = 1L;

    // Represent the number of bytes occupied when writes ExecutionAttemptID to the ByteBuf.
    // It is the sum of one ExecutionGraphID type(executionGraphId), one ExecutionVertexID
    // type(executionVertexId) and one int type(attemptNumber).
    private static final int BYTE_BUF_LEN = ConsumeItemID.SIZE + 4;

    private final ConsumeItemID consumeItemId;

    private final int attemptNumber;

    public ConsumeAttemptID(
    		ConsumeItemID consumeItemId,
            int attemptNumber) {
        checkArgument(attemptNumber >= 0);
        this.consumeItemId = checkNotNull(consumeItemId);
        this.attemptNumber = attemptNumber;
    }

    public ConsumeItemID getConsumeItemId() {
        return consumeItemId;
    }

    public int getAttemptNumber() {
        return attemptNumber;
    }

    public void writeTo(ByteBuf buf) {
        consumeItemId.writeTo(buf);
        buf.writeInt(this.attemptNumber);
    }

    public static ConsumeAttemptID fromByteBuf(ByteBuf buf) {
        return new ConsumeAttemptID(
        		ConsumeItemID.fromByteBuf(buf),
                buf.readInt());
    }

    public static int getByteBufLength() {
        return BYTE_BUF_LEN;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (obj != null && obj.getClass() == getClass()) {
            ConsumeAttemptID that = (ConsumeAttemptID) obj;
            return that.consumeItemId.equals(this.consumeItemId)
                    && that.attemptNumber == this.attemptNumber;
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return Objects.hash(consumeItemId, attemptNumber);
    }

    @Override
    public String toString() {
        return String.format("%s_%s_%d", consumeItemId, attemptNumber);
    }

    public static ConsumeAttemptID randomId() {
        return new ConsumeAttemptID(new ConsumeItemID(), 0);
    }
}
